﻿using System;
using System.Collections.Generic;
using System.Text;
using Development.Tools;
using System.Reflection;
using Development.DataEntity;
using fastJSON;
using System.Configuration;
using FastReflectionLib;



namespace Development.DbData
{
    public class DbDataHelper
    {
        private static Dictionary<string, long> _tableCountCache = new Dictionary<string, long>();
        private static readonly object _flagLock = new object();
        /// <summary>
        /// 检查程序集中的所有表是否创建
        /// </summary>
        public static void CheckAssemblyAllTables()
        {
            var types = Assembly.GetEntryAssembly().GetTypes();
            var baseType = typeof(DataEntityBase);
            foreach (var ty in types)
            {
                if (ty.BaseType.BaseType == baseType ||ty.BaseType == baseType)
                {
                    DataEntityBase obj = Global.CreateObject(ty) as DataEntityBase;
                    if (obj != null)
                    {
                        CheckTable(obj);
                    }
                }
            }
        }
        /// <summary>
        /// 检查数据表，如果不存在就创建
        /// </summary>
        /// <param name="table"></param>
        public static void CheckTable(DataEntityBase table)
        {

            DbProvider provider = CreateDbProvider(ConfigUtil.GetConnectionSetting(table.GetDbName()));
            if (provider == null)
            {
                return;
            }
            string dbName = "";
            if (!provider.Open())
            {
                string connectStr = ConfigUtil.GetConnectionSetting(table.GetDbName()).ConnectionString.ToLower();
                string[] temp = connectStr.Split(';');
                connectStr = "";
                foreach(var t in temp)
                {
                    if(!t.Contains("database"))
                    {
                        connectStr += t+";";
                    }
                    else
                    {
                        dbName = t.Replace("database=", "");
                    }
                }
                provider = CreateDbProvider(ConfigUtil.GetConnectionSetting(table.GetDbName()), connectStr);
                if(provider.Open())
                {
                    string s = string.Format("CREATE DATABASE `{0}` CHARACTER SET utf8 COLLATE utf8_general_ci", dbName);
                    provider.ExecuteNonQuery(s);
                    provider.ExecuteNonQuery(string.Format("use {0}",dbName));
                }
                else
                {
                    return;
                }
            }
            Type tableType = table.GetType();
            string TableName = tableType.Name;

            //检查表是否存在
            bool isTableExist = provider.IsTableExist(TableName);
            if (!isTableExist)
            {
                List<DbColum> cols = DbColum.CreateDbColums(tableType);
                StringBuilder command = new StringBuilder();
                command.AppendFormat("CREATE TABLE {0}(", TableName);

                foreach (DbColum c in cols)
                {
                    command.AppendFormat(DbColum.CreateColumSql(c) + ",");
                }
                command.Remove(command.Length - 1, 1);
                command.AppendFormat(")CharSet=utf8;");

                //创建表
                provider.ExecuteNonQuery(command.ToString());
                Log.WriteLog(ELogLevel.LOG_INFO, string.Format("Create table {0} Success", TableName));
            }
            string sql = "select count(*) from " + TableName;
            object ret = provider.ExecuteScalar(sql);
            int count = 0;
            int.TryParse(ret.ToString(), out count);
            _tableCountCache.Add(TableName, count);
            provider.Close();
        }


        /// <summary>
        /// db工厂方法
        /// </summary>
        /// <param name="providerName"></param>
        /// <returns></returns>
        public static DbProvider CreateDbProvider(ConnectionStringSettings dbSetting)
        {
            DbProvider provider = null;
            if (dbSetting.ProviderName == "MySqlDataProvider")
            {
                provider = new DbMySqlProvider(dbSetting.ConnectionString);
            }
            return provider;
        }

        /// <summary>
        /// db工厂方法
        /// </summary>
        /// <param name="providerName"></param>
        /// <returns></returns>
        public static DbProvider CreateDbProvider(ConnectionStringSettings dbSetting,string connectStr)
        {
            DbProvider provider = null;
            if (dbSetting.ProviderName == "MySqlDataProvider")
            {
                provider = new DbMySqlProvider(connectStr);
            }
            return provider;
        }

        public static DbProvider CreateDbProvider(Type type)
        {
            DbProvider provider = null;
            DbDataAttribute attr = Attribute.GetCustomAttribute(type, typeof(DbDataAttribute)) as DbDataAttribute;
            if(attr != null)
            {
                var config = ConfigUtil.GetConnectionSetting(attr.DbName);
                if(config != null)
                {
                    provider = CreateDbProvider(config);
                }
            }
            return provider;
        }

        /// <summary>
        /// 是否为基本属性
        /// </summary>
        /// <param name="fd"></param>
        /// <returns></returns>
        private static bool IsBasePropertyType(PropertyInfo fd)
        {
            if (fd.PropertyType == typeof(Int32)
                || fd.PropertyType == typeof(float)
                || fd.PropertyType == typeof(string)
                || fd.PropertyType == typeof(long)
                || fd.PropertyType == typeof(Int64)
                || fd.PropertyType == typeof(UInt64)
                || fd.PropertyType == typeof(Boolean)
                || fd.PropertyType == typeof(Decimal)
                || fd.PropertyType == typeof(Double)
                || fd.PropertyType == typeof(UInt32)
                || fd.PropertyType == typeof(Single)
                || fd.PropertyType == typeof(Int16)
                || fd.PropertyType == typeof(UInt16)
                || fd.PropertyType == typeof(Byte)
                || fd.PropertyType == typeof(Byte[])
                || fd.PropertyType == typeof(Guid)
                                            )
            {
                return true;
            }
            return false;
        }

        /// <summary>
        /// set obj property
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="dbVal"></param>
        /// <param name="fd"></param>
        public static void SetProperty(object obj,object dbVal, PropertyInfo fd)
        {
            object val = dbVal;
            if (IsBasePropertyType(fd))
            {
                if (val == DBNull.Value)
                {
                    val = fd.PropertyType.IsValueType ? Activator.CreateInstance(fd.PropertyType) : null;
                }
                if (fd.PropertyType == typeof(Single))
                {
                    val = float.Parse(val.ToString());
                }
                fd.FastSetValue(obj, val);
            }
            else if (fd.PropertyType == typeof(DateTime))
            {
                fd.FastSetValue(obj, DateTime.Parse(val.ToString()));
            }
            else
            {
                if (val != DBNull.Value)
                {
                    val = fastJSON.JSON.ToObject(val.ToString(), fd.PropertyType);
                }
                else
                {
                    val = null;
                }
                fd.FastSetValue(obj, val);
            }
        }

        /// <summary>
        /// db to obj
        /// </summary>
        /// <param name="db"></param>
        /// <param name="obj"></param>
        public static void DbToObj(DbProvider db,object obj)
        {
            Type type = obj.GetType();
            PropertyInfo[] fields = type.GetProperties();
            for(int i = 0; i < fields.Length;++i)
            {
                PropertyInfo fd = fields[i];
                object dbVal = db.Read(fd.Name);
                SetProperty(obj, dbVal, fd);
            }
        }

        /// <summary>
        /// 获取entity 插入sql
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        public static string GetEntityInsertSql(DataEntityBase entity)
        {
            Type type = entity.GetType();
            PropertyInfo[] fields = type.GetProperties();
            string values = "";
            for (int i = 0; i < fields.Length; ++i)
            {
                PropertyInfo fd = fields[i];
                values += GetSqlValStringByProperties(entity, fd);
                if (i != fields.Length - 1)
                {
                    values += ",";
                }
            }
            return string.Format("insert into {0} values ({1})", entity.GetType().Name, values);
        }

        /// <summary>
        /// 获取enity更新sql
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        public static string GetEntityUpdateSql(DataEntityBase entity)
        {
            string sets = "";
            Type type = entity.GetType();
            PropertyInfo[] fields = type.GetProperties();
            for (int i = 0; i < fields.Length; ++i)
            {
                PropertyInfo fd = fields[i];
                sets += fd.Name + "=" + GetSqlValStringByProperties(entity, fd);
                if(i != fields.Length - 1)
                {
                    sets += ",";
                }
            }
            return string.Format("update {0} set {1} where ID = {2}", entity.GetType().Name, sets, entity.ID);
        }

        private static string GetSqlValStringByProperties(object obj,PropertyInfo fd)
        {
            if (fd.PropertyType == typeof(Int32)
                || fd.PropertyType == typeof(float)
                || fd.PropertyType == typeof(long)
                || fd.PropertyType == typeof(Int64)
                || fd.PropertyType == typeof(UInt64)
                || fd.PropertyType == typeof(Boolean)
                || fd.PropertyType == typeof(Decimal)
                || fd.PropertyType == typeof(Double)
                || fd.PropertyType == typeof(UInt32)
                || fd.PropertyType == typeof(Single)
                || fd.PropertyType == typeof(Int16)
                || fd.PropertyType == typeof(UInt16)
                || fd.PropertyType == typeof(Byte)
                || fd.PropertyType == typeof(Byte[])
                                           )
            {
                return string.Format("{0}", fd.FastGetValue(obj));
            }
            else if(fd.PropertyType == typeof(string)
                    || fd.PropertyType == typeof(Guid)
                    || fd.PropertyType == typeof(DateTime)
                )
            {
                return string.Format("'{0}'", fd.FastGetValue(obj));
            }
            else
            {
                return string.Format("'{0}'", JSON.ToJSON(fd.FastGetValue(obj)));
            }
        }

        /// <summary>
        /// 获取entiy 删除sql
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        public static string GetEntityDeleteSql(DataEntityBase entity)
        {
            return string.Format("delete from {0} where ID = {1}", entity.GetType().Name, entity.ID);;
        }

        /// <summary>
        ///从数据库里面获取数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public static T GetDbEntity<T>(DbProvider dbProvider,long id) where T : DataEntityBase,new()
        {
            string sql =string.Format("select * from {0} where ID = {1}",typeof(T).Name,id);
            T t = null;
            if (dbProvider.ExecuteQuery(sql))
            {
                if(dbProvider.Read())
                {
                    t = new T();
                    DbToObj(dbProvider, t);
                }
            }
            dbProvider.EndExecuteQuery();
            return t;
        }

        public static List<T> GetDbEntityList<T>(DbProvider dbProvider) where T : DataEntityBase, new()
        {
            string sql = string.Format("select * from {0}", typeof(T).Name);
            List<T> list = new List<T>();
            if (dbProvider.ExecuteQuery(sql))
            {
                while (dbProvider.Read())
                {
                    T t = new T();
                    DbToObj(dbProvider, t);
                    list.Add(t);
                }
            }
            dbProvider.EndExecuteQuery();
            return list;
        }

        public static List<DataEntityBase> GetDbEntityList(Type type)
        {
            string sql = string.Format("select * from {0}", type.Name);
            List<DataEntityBase> list = new List<DataEntityBase>();
            var dbProvider = CreateDbProvider(type);
            if(dbProvider != null)
            {
                if(dbProvider.Open())
                {
                    if (dbProvider.ExecuteQuery(sql))
                    {
                        while (dbProvider.Read())
                        {
                            DataEntityBase obj = Global.CreateObject(type) as DataEntityBase;
                            DbToObj(dbProvider, obj);
                            list.Add(obj);
                        }
                    }
                }
                dbProvider.EndExecuteQuery();
                dbProvider.Close();
            }
            return list;
        }
         public static List<T> GetDbEntityList<T>(List<long> ids) where T : DataEntityBase
         {
             Type type = typeof(T);
             string sql = string.Format("select * from {0}", type.Name);
             sql += " where ID in (";
             for(int i = 0; i < ids.Count;++i)
             {
                 sql += ids[i].ToString();
                 if(i != ids.Count-1)
                 {
                     sql += ",";
                 }
                 else
                 {
                     sql += ")";
                 }
             }

             List<T> list = new List<T>();
             var dbProvider = CreateDbProvider(type);
             if (dbProvider != null)
             {
                 if (dbProvider.Open())
                 {
                     if (dbProvider.ExecuteQuery(sql))
                     {
                         while (dbProvider.Read())
                         {
                             var obj = Global.CreateObject(type) as T;
                             DbToObj(dbProvider, obj);
                             list.Add(obj);
                         }
                     }
                 }
                 dbProvider.EndExecuteQuery();
                 dbProvider.Close();
             }
             return list;
         }

        public static void  RemoveIds<T>(List<T> entitys) where T : DataEntityBase
        {
            Type type = typeof(T);
            string sql = string.Format("delete from {0}", type.Name);
            sql += " where ID in (";
            for (int i = 0; i < entitys.Count; ++i)
            {
                sql += entitys[i].ID.ToString();
                if (i != entitys.Count - 1)
                {
                    sql += ",";
                }
                else
                {
                    sql += ")";
                }
            }

            List<T> list = new List<T>();
            var dbProvider = CreateDbProvider(type);
            if (dbProvider != null)
            {
                if (dbProvider.Open())
                {
                    dbProvider.ExecuteNonQuery(sql);
                }
                dbProvider.Close();
            }
        }



        public static List<Type> GetTypesFromCurrAssemblyByBase(Type baseType)
        {
            List<Type> types = new List<Type>();
            var typeArray = Assembly.GetEntryAssembly().GetTypes();
            foreach (var ty in typeArray)
            {
                if (ty.BaseType == baseType)
                {
                    types.Add(ty);
                }
            }
            return types;
        }

        public static long GetNextDbID<T>() where T : DataEntityBase
        {
            string tableName = typeof(T).Name;
            lock(_flagLock)
            {
                return (_tableCountCache[tableName]++) + 1000;
            }
        }

        public static int SelectCount(DbProvider provider,string sql)
        {
            object ret = provider.ExecuteScalar(sql);
            int count = 0;
            int.TryParse(ret.ToString(), out count);
            return count;
        }


        
    }
}
